package org.example.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.example.entity.*;
import org.example.models.R;
import org.example.service.AttendanceRecordService;
import org.example.service.OvertimeApplicationService;
import org.example.service.VacationBalanceService;
import org.example.service.WorkingCalendarService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

/**
 * @Author: 鸢一折纸
 * @Email: 3498881048@qq.com
 * @Date: 2025/09/11 10:37
 * @Description:
 **/

@RestController
@RequestMapping("/overtimeApplication")
public class OvertimeApplicationController {

    @Autowired
    private OvertimeApplicationService overtimeApplicationService;
    @Autowired
    private VacationBalanceService vacationBalanceService;
    @Autowired
    private AttendanceRecordService attendanceRecordService;
    @Autowired
    private WorkingCalendarService workingCalendarService;

    @GetMapping("/page")
    public R<Page<OvertimeApplication>> getAllOvertimeApplication(
            @RequestParam(name = "current", defaultValue = "1") int current,
            @RequestParam(name = "size", defaultValue = "4") int size,
            @RequestAttribute("userId") Integer userId,
            @RequestParam(name = "applicationDate", required = false) String applicationDate
    ) {
        QueryWrapper<OvertimeApplication> query = new QueryWrapper<>();
        query.lambda().eq(OvertimeApplication::getUserId, userId);

        if (applicationDate != null && !applicationDate.isEmpty()) {
            query.lambda().like(OvertimeApplication::getApplicationDate, applicationDate);
        }

        query.lambda().orderByDesc(OvertimeApplication::getUpdatedAt);

        Page<OvertimeApplication> page = new Page<>(current, size);

        overtimeApplicationService.page(page, query);
        return R.success(page);
    }

    @GetMapping("/overtimeDetails")
    public R<Page<OvertimeApplication>> overtimeDetails(
            @RequestParam(name = "current", defaultValue = "1") int current,
            @RequestParam(name = "size", defaultValue = "4") int size,
            @RequestAttribute("userId") Integer userId,
            @RequestParam(name = "applicationDate", required = false) String applicationDate
    ) {
        QueryWrapper<OvertimeApplication> query = new QueryWrapper<>();
        query.lambda().eq(OvertimeApplication::getUserId, userId);

        if (applicationDate != null && !applicationDate.isEmpty()) {
            query.lambda().eq(OvertimeApplication::getApplicationDate, applicationDate);
        }

        query.lambda().orderByDesc(OvertimeApplication::getUpdatedAt);

        Page<OvertimeApplication> page = new Page<>(current, size);

        overtimeApplicationService.page(page, query);
        return R.success(page);
    }

    @PostMapping("/insert")
    public R<String> insert(@RequestBody List<OvertimeApplication> overtimeApplications,  @RequestAttribute("userId") Integer userId) {
        if (overtimeApplications == null || overtimeApplications.isEmpty()) {
            return R.error("请至少提交一条加班记录");
        }

        for (OvertimeApplication overtime : overtimeApplications) {
            if (isDuplicateOvertimeApplication(overtime, userId)) {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                return R.error("存在冲突的加班申请");
            }

            R<String> checkResult = checkWorkdayAndShift(overtime);
            if (!Integer.valueOf(200).equals(checkResult.getCode())) {
                return checkResult;
            }
        }

        for (OvertimeApplication overtime : overtimeApplications) {
            overtime.setUserId(userId);
        }

        boolean saveSuccess = overtimeApplicationService.saveBatch(overtimeApplications);
        if (!saveSuccess) {
            return R.error("加班申请保存失败");
        }

        // 更新考勤记录状态为加班
        for (OvertimeApplication overtime : overtimeApplications) {
            updateAttendanceRecordsForOvertime(overtime, userId);
        }

        // 将加班时长转换为调休天数，并更新员工调休余额
        handleVacationBalance(overtimeApplications, userId);

        return R.success("加班申请成功，调休已生成");
    }

    /**
     * 检查是否存在重复或冲突的加班申请
     */
    private boolean isDuplicateOvertimeApplication(OvertimeApplication overtime, Integer userId) {
        Date newStartTime = overtime.getStartTime();
        Date newEndTime = overtime.getEndTime();

        // 获取加班日期
        Date overtimeDate = getDateFromDateTime(newStartTime);

        // 查询同一天的所有加班记录
        QueryWrapper<OvertimeApplication> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId)
                .eq("DATE(start_time)", overtimeDate);

        // 如果是更新操作，需要排除当前记录本身
        if (overtime.getOvertimeId() != null) {
            queryWrapper.ne("overtime_id", overtime.getOvertimeId());
        }

        List<OvertimeApplication> existingApplications = overtimeApplicationService.list(queryWrapper);

        // 判断新申请的时段
        String newPeriod = determineOvertimePeriod(newStartTime, newEndTime);

        for (OvertimeApplication existing : existingApplications) {
            Date existingStartTime = existing.getStartTime();
            Date existingEndTime = existing.getEndTime();
            String existingPeriod = determineOvertimePeriod(existingStartTime, existingEndTime);

            // 检查完全相同的时段
            if (newPeriod.equals(existingPeriod)) {
                return true; // 相同时段重复
            }

            // 检查时段冲突
            if ("全天".equals(newPeriod) || "全天".equals(existingPeriod)) {
                return true; // 全天与任何时段都冲突
            }

            // 检查具体时间是否冲突
            if (isTimeOverlap(newStartTime, newEndTime, existingStartTime, existingEndTime)) {
                return true; // 时间段重叠
            }
        }

        return false;
    }

    /**
     * 检查两个时间段是否重叠
     */
    private boolean isTimeOverlap(Date start1, Date end1, Date start2, Date end2) {
        return start1.before(end2) && start2.before(end1);
    }

    /**
     * 检查加班日期是否为工作日或节假日调班
     */
    private R<String> checkWorkdayAndShift(OvertimeApplication overtime) {
        Date overtimeDate = overtime.getStartTime();

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String date = sdf.format(overtimeDate);

        // 查询工作日历表中对应的记录
        QueryWrapper<WorkingCalendar> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("date_time", date);

        WorkingCalendar workCalendar = workingCalendarService.getOne(queryWrapper);

        if (workCalendar != null) {
            String workingCondition = (String) workCalendar.getWorkingCondition();
            if ("节假日调班".equals(workingCondition)) {
                return R.error("不能在节假日调班日加班");
            }
            // 如果是公休日或节假日，允许加班
        } else {
            // 没有记录说明是正常工作日，不允许加班
            return R.error("不能在工作日加班");
        }

        return R.success("检查通过");
    }

    /**
     * 更新考勤记录状态为加班
     */
    private void updateAttendanceRecordsForOvertime(OvertimeApplication overtime, Integer userId) {
        // 获取加班开始和结束时间
        Date startTime = overtime.getStartTime();
        Date endTime = overtime.getEndTime();

        // 判断加班时段（上午/下午/全天）
        String overtimePeriod = determineOvertimePeriod(startTime, endTime);

        // 获取加班日期
        Date overtimeDate = getDateFromDateTime(startTime);

        // 更新考勤记录
        QueryWrapper<AttendanceRecord> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id", userId)
                .eq("record_date", overtimeDate);

        AttendanceRecord record = attendanceRecordService.getOne(wrapper);
        if (record == null) {
            // 如果没有考勤记录，创建新记录
            record = new AttendanceRecord();
            record.setUserId(userId);
            record.setRecordDate(overtimeDate);
            setAttendanceStatus(record, overtimePeriod, "加班");
            attendanceRecordService.save(record);
        } else {
            // 更新现有记录
            setAttendanceStatus(record, overtimePeriod, "加班");
            attendanceRecordService.updateById(record);
        }
    }

    /**
     * 根据时段设置考勤状态
     */
    private void setAttendanceStatus(AttendanceRecord record, String period, String status) {
        if ("上午".equals(period)) {
            record.setMorningStatus(status);
        } else if ("下午".equals(period)) {
            record.setAfternoonStatus(status);
        } else if ("全天".equals(period)) {
            record.setMorningStatus(status);
            record.setAfternoonStatus(status);
        }
    }

    /**
     * 根据开始和结束时间判断加班时段
     */
    private String determineOvertimePeriod(Date startTime, Date endTime) {
        Calendar startCal = Calendar.getInstance();
        startCal.setTime(startTime);
        Calendar endCal = Calendar.getInstance();
        endCal.setTime(endTime);

        int startHour = startCal.get(Calendar.HOUR_OF_DAY);
        int endHour = endCal.get(Calendar.HOUR_OF_DAY);

        if (startHour >= 1 && endHour <= 12) {
            return "上午";
        } else if (startHour >= 12 && endHour <= 22) {
            return "下午";
        } else {
            return "全天";
        }
    }

    /**
     * 从日期时间中提取日期部分
     */
    private Date getDateFromDateTime(Date dateTime) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(dateTime);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return cal.getTime();
    }

    private void handleVacationBalance(List<OvertimeApplication> overtimeApplications, Integer userId) {
        // 查询当前员工的调休余额
        VacationBalance balance = vacationBalanceService.getOne(new QueryWrapper<VacationBalance>()
                .eq("user_id", userId));

        BigDecimal totalVacationDays = BigDecimal.ZERO;
        BigDecimal HOURS_PER_DAY = BigDecimal.valueOf(8);

        // 计算总调休天数（加班时长 / 8 小时）
        for (OvertimeApplication overtime : overtimeApplications) {
            BigDecimal durationHours = overtime.getDuration(); // 加班时长（小时）
            BigDecimal vacationDays = durationHours.divide(HOURS_PER_DAY, 2, BigDecimal.ROUND_HALF_UP);
            totalVacationDays = totalVacationDays.add(vacationDays);
        }

        // 如果没有余额记录，则新建；否则更新
        if (balance == null) {
            balance = new VacationBalance();
            balance.setUserId(userId);
            balance.setVacationDays(totalVacationDays);
            balance.setSubmitAt(new Date());
            balance.setUpdatedAt(new Date());
            vacationBalanceService.save(balance);
        } else {
            balance.setVacationDays(balance.getVacationDays().add(totalVacationDays));
            balance.setUpdatedAt(new Date());
            vacationBalanceService.updateById(balance);
        }
    }

}
